#ifndef _DEF_H
#define _DEF_H


class TableInfo;

struct TableCol
{
  int tableID;
  int colID;
};

typedef unsigned short _field_offset;
typedef unsigned char _byte;
typedef int (*db_callback) (Db * secondary, const Dbt * key, const Dbt * data,
			    Dbt * result);

static map < string, TableCol > colname2tc;
static vector < TableInfo * >table_infos;
static map < string, TableInfo > table2info;
static DbEnv *env = NULL;
//#define MAX_COL  256
//#define BL_CONV(x)  ( (((x)&0xff)<<24)|((((x)>>8)&0xff)<<16)|((((x)>>16)&0xff)<<8)| (((x)>>24)&0xff)  )

//__builtin_*** GCC only
#define BL_CONV(x)  __builtin_bswap32(x) 
enum DataType
{
  INTEGER,
  VARCHAR
};

struct ColType
{
  DataType type;
  _field_offset size;
  _field_offset offset;
  MyDb *db;
    ColType (const string & ts)
  {
    offset = 0;
    db = NULL;
    if (ts.compare (0, 3, "INT") == 0)
      {
	type = INTEGER;
	size = 4;
      }
    else if (ts.compare (0, 7, "VARCHAR") == 0)
      {
	type = VARCHAR;
	size = atoi (ts.substr (8, ts.length () - 9).c_str ());
      }
    else
      {
	cerr << "ERROR: unknown type '" << ts << "'" << endl;
      }
  }
  void dump (ostream & os)
  {
    os << type << '\t' << size << '\t' << offset << endl;
  }
  inline bool hasDb ()
  {
    return (db != NULL);
  }
};

class TableInfo
{
public:
  int tableID;
    vector < string > colName;
    vector < ColType > colType;
    vector < int >pKey_idx;
  _field_offset entrySize;
  _field_offset keySize;
   map < string, int >name2idx;
  string tableName;
  MyDb *db;
  size_t row_count;
    TableInfo ():db (0), keySize (0), entrySize (0)
  {
  }
  TableInfo (const string & table_name, const vector < string > &column_name,
	     const vector < string > &column_type,
	     const vector < string > &primary_key)
  {
	row_count = 0;
    colName = column_name;
    tableName = table_name;
    _field_offset offset = 0;
    tableID = table_infos.size ();
    for (int i = 0; i < colName.size (); i++)
      {
	name2idx[colName[i]] = i;
	TableCol tc;
	tc.tableID = tableID;
	tc.colID = i;
	colname2tc[colName[i]] = tc;
	ColType ct (column_type[i]);
	ct.offset = offset;
	offset += ct.size;
	colType.push_back (ct);
      }
    entrySize = offset;
    keySize = 0;
    for (int i = 0; i < primary_key.size (); i++)
      {
	int idx = name2idx[primary_key[i]];
	pKey_idx.push_back (idx);
	keySize += colType[i].size;
      }
	if(!env){
	  env = new DbEnv(0);
	  env->set_cachesize(2,0,1);
	  env->open("./data/", DB_CREATE|DB_INIT_MPOOL|DB_PRIVATE,0);
	}
    string path ("");
    string filename (tableName + string (".db"));
    db = new MyDb (env, path, filename, false);
	if(primary_key.size()==1){
      db->colIdx = name2idx[primary_key[0]];
	}
  }

  void createSecondaryDb (const string & keyCol, db_callback callback)
  {
    if (colType[name2idx[keyCol]].hasDb ())
      {
	cerr << "WARNING: " << keyCol << " already has a secondary db." <<
	  endl;
	return;
      }
    if (pKey_idx.size () == 1 && colName[pKey_idx[0]] == keyCol)
      {
	cerr << "WARNING: " << keyCol << " is primary key." << endl;
      }
    string path ("");
    string fn (tableName + "_" + keyCol + ".db");
    MyDb *sdb = new MyDb (env, path, fn, true);
    db->associate (NULL, sdb, callback, 0);
    sdb->ti = db->ti;
    sdb->colIdx = name2idx[keyCol];
    colType[name2idx[keyCol]].db = sdb;
	cout<<"INFO: secondary DB created: "<<fn<<endl;
  }

  ~TableInfo ()
  {
    //delete db;
  }

  void dump (ostream & os)
  {
    os << "DUMP: ID=" << tableID << " " << tableName << " ...size=" <<
      entrySize << ", keySize=" << keySize << endl;
    for (int i = 0; i < colName.size (); i++)
      {
	os << colName[i] << '\t';
	colType[i].dump (os);
      }
    os << endl;
  }

};

bool cmpTableInfo(const TableInfo &ti1, const TableInfo &ti2){
  return (ti1.row_count<ti2.row_count);
}
bool cmpTableInfoByIdx(int x,int y){
  return (table_infos[x]->row_count<table_infos[y]->row_count  );
}
enum CondType
{
  CT_CONST_STRING,
  CT_CONST_INT,
  CT_LESS,
  CT_GREATER,
  CT_JOIN
};
struct CondInfo
{
  int type;
  int op1_table;		//index of table
  int op1;			//index of column
  int op2_table;
  string str_op2;		//string constant
  int int_op2;			//index of column or constant

};


struct ProductState
{
  stack < Dbc * >cursors;
  vector < _byte * >bufs;
  vector < int >tc2tb;
  vector < int >tables;
  vector < TableCol > tc;
  vector < vector<_field_offset > > fo_buf;
  vector < MyDb * > dbs;
  vector <CondInfo> condInfo;
/*
 *  In order to reduce the overhead of opProductNext
 *  we buffer some of the results in the vector
 */
  vector < string > results;
  bool done;
};

#endif
